/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 panic.cpp

	$Header: /heroes4/panic.cpp $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "panic.h"

#include "adaptor_handler.h"
#include "angle.h"
#include "battlefield.h"
#include "bound_handler.h"
#include "can_cast.h"
#include "combat_creature.h"
#include "combat_path_finder.h"
#include "external_string.h"
#include "function_handler.h"
#include "spell.h"
#include "random.h"
#include "replace_keywords.h"
#include "spell_properties.h"

extern t_external_string const k_combat_action_cause_effect;

// ------------------------------------------------------------
// determine where to move a creature if it panics
// ------------------------------------------------------------
static bool find_panic_destination( t_combat_path_finder& path_finder, int base_angle,
								    t_map_point_2d const& start_point,
									t_map_point_2d& result )
{
	// clear array
	t_map_point_2d				point;
	t_map_point_2d				delta;
	int							row_end;
	t_combat_path_data const*	ptr;
	std::vector<t_map_point_2d> points;
	int							angle;
	int                         distance;
	int                         farthest;

	point.row = path_finder.get_size();
	while (point.row--)
	{
		point.column = path_finder.get_row_start( point.row );
		row_end = path_finder.get_row_end( point.row );
		ptr = &path_finder.get_data( point );
		for (; point.column < row_end; point.column++, ptr++)
		{
			if (!ptr->visited)
				continue;
			if (ptr->flight_cost > 0)
				continue;
			if (ptr->move_cost > 600)
				continue;
			angle = get_angle( start_point, point );
			angle = get_difference( angle, base_angle );
			if (angle > 20)
				continue;
			distance = ptr->move_cost;
			if (!points.empty())
			{
				if (distance < farthest)
					continue;
				if (distance > farthest)
					points.clear();
			}
			farthest = distance;
			points.push_back( point );
		}
	}
	if (points.empty())
		return false;
	
	int choice = random( points.size() );
	
	result = points[choice];
	return true;
}

// ------------------------------------------------------------
// start a panic move
// ------------------------------------------------------------
namespace
{
	class t_panic_move : public t_handler_base_1<t_window*>
	{
	public:
		t_panic_move( t_combat_creature* creature, t_combat_path const& path, t_combat_action_message const & message );
		virtual void operator()( t_window* );
	protected:
		t_combat_creature_ptr   m_creature;
		t_combat_action_message m_message;
		t_combat_path			m_path;
	};
};

t_panic_move::t_panic_move( t_combat_creature* creature, t_combat_path const& path, t_combat_action_message const & message )
{
	m_creature = creature;
	m_message = message;
	m_path = path;
}

void t_panic_move::operator()( t_window* )
{
	t_battlefield&            battlefield = m_creature->get_battlefield();
	t_combat_creature_handler handler;
	t_map_point_2d            destination = m_path.front();

	handler = bound_handler( battlefield, &t_battlefield::end_walk );
	battlefield.begin_move( *m_creature, m_path, destination, handler, m_message );
}

// ------------------------------------------------------------
// start a panic move
// ------------------------------------------------------------
bool t_combat_creature::begin_panic( t_combat_creature const& attacker )
{
	if (!can_affect( attacker, k_spell_panic, *this ))
		return false;
	if (get_number() == 0)
		return false;
	if (is_active( k_spell_binding ) || is_active( k_spell_frozen ))
		return false;

	t_combat_path_finder path_finder( *this, false );
	t_map_point_2d       destination;
	int                  base_angle = get_angle( attacker.get_footprint_center(),
		                                         get_footprint_center() );

	if (!find_panic_destination( path_finder, base_angle, get_cell_position(),
		                         destination ))
		return false;

	t_combat_path    path;

	if (!path_finder.get_path( destination, path ))
		return false;

	t_window_handler handler;
	t_combat_action_message message( attacker, replace_keywords( k_combat_action_cause_effect, "%spell", get_short_spell_name( k_spell_panic ) ) );

	handler = new t_panic_move( this, path, message );
	m_battlefield.start_animation( *this, k_spell_panic, handler, message );
	return true;
}

